package se.webbzon.oschi01.worldarea;

import java.util.ArrayList;
import java.util.Collection;

import se.webbzon.boltzmann.debug.Debug;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public class WorldEngine {
	
	// The current world area associated with this engine
	private WorldArea world;
	
	// The world to switch to on the end of the current step
	private WorldArea switchToWorld;
	
	// A listener used to to listen to world area changes
	private final WorldAreaListener worldAreaListener;
	
	// A collection of listeners to this engine
	private final Collection<WorldEngineListener> listeners;
	
	// The thread used for global stepping
	private final StepThread stepThread;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new WorldEngine associated with a given WorldArea. **/
	public WorldEngine(WorldArea world) {
		this();
		setWorld(world);
	}
	
	/** Creates a new WorldEngine. **/
	public WorldEngine() {
		switchToWorld = null;
		worldAreaListener = new Listener();
		listeners = new ArrayList<WorldEngineListener>();
		stepThread = new StepThread();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Adds a new listener to this world engine. **/
	public synchronized boolean addListener(WorldEngineListener listener) {
		int d;
		Debug.debugCollection(this, listeners);
		return listeners.add(listener);
	}
	
	/** Removes a listener from this world engine. **/
	public synchronized boolean removeListener(WorldEngineListener listener) {
		return listeners.remove(listener);
	}
	
	/** Removes all listeners from this world engine. **/
	public synchronized void clearListeners() {
		listeners.clear();
	}
	
	/** Returns the currently running world area. **/
	public synchronized WorldArea getWorld() {
		return world;
	}
	
	/** Sets the currently running world area. **/
	public synchronized void setWorld(WorldArea world) {
		if (this.world == null)
			switchWorldArea(world);
		else {
			if (this.world.idling())
				switchWorldArea(world);
			else
				switchToWorld = world;
		}
	}
	
	/** Performs a global step for the currently running world area. **/
	public void performStep() {
		stepThread.run();
	}
	
	/** Performs the onPreDraw event for all instances in the
	 * currently running world area. **/
	public synchronized void performPreDraw(WorldCameraPane background) {
		if (world != null)
			world.performPreDraw(background);
	}
	
	/** Performs the onPreDraw event for all instances in the
	 * currently running world area. **/
	public synchronized void performPostDraw(WorldCameraPane hud) {
		if (world != null)
			world.performPostDraw(hud);
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Performs a global step for the currently running world area. **/
	private synchronized void step() {
		if (world != null) {
			world.performStep();
			if (switchToWorld != null)
				switchWorldArea(switchToWorld);
		}
	}
	
	/** Switch the current world area to the given world area. **/
	private void switchWorldArea(WorldArea world) {
		switchToWorld = null;
		if (this.world != null)
			this.world.performOnAreaLeave();
		this.world = world;
		if (world != null) {
			world.addListener(worldAreaListener);
			world.performOnAreaEnter();
		}
		notifyOnSetWorldArea(world);
	}
	
	/** Notifies to all listeners that the current world area has been
	 * changed into the given world area.**/
	private void notifyOnSetWorldArea(WorldArea world) {
		for (WorldEngineListener listener : listeners)
			listener.onSetWorldArea(world);
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** An implementation of a WorldAreaListener. **/
	private class Listener implements WorldAreaListener {
		
		/*============================================================
		Public Methods
		============================================================*/

		@Override public void onInstanceAdd(WorldArea source, WorldObject instance) {};

		@Override public void onInstanceRemove(WorldArea source, WorldObject instance) {};

		@Override public void onInstancesClear(WorldArea source) {};
		
		@Override public void onGoto(WorldArea source, WorldArea destination) {
			source.removeListener(worldAreaListener);
			setWorld(destination);
		}

	}
	
	/** The thread used for performing global steps. **/
	private class StepThread extends Thread {
		
		/*============================================================
		Public Methods
		============================================================*/
		
		@Override public void run() {
			step();
		}
		
	}

}
